package plugins

import (
	"errors"
	"fmt"
	"testing"

	"github.com/spf13/cobra"
)

// WithValidPlugin returns a stub for a valid plugin
func WithValidPlugin(t testing.TB) *Plugin {
	return &Plugin{
		Name:    "echo",
		Command: []string{WithEcho(t)},
		Version: "0.133.7",
		Completion: PluginCompletion{
			Enabled: true,
		},
		Description: "",
		Complete: func(cmd *cobra.Command, args []string) (err error) {
			_, _ = fmt.Fprintf(cmd.OutOrStdout(), "--help\n")
			return nil
		},
		isValid: true,
	}
}

func TestPlugin_Validate(t *testing.T) {
	t.Run("ok", func(sub *testing.T) {
		if err := WithValidPlugin(sub).Validate(); err != nil {
			t.Errorf("got error: %v", err)
		}
	})

	t.Run("invalid", func(sub *testing.T) {
		if err := (&Plugin{}).Validate(); !errors.Is(ErrCommandIsExpected, err) {
			sub.Errorf("expected: `%v`, got: `%v`", ErrCommandIsExpected, err)
		}
	})
}

func TestPlugin_completionApp(t *testing.T) {
	for _, test := range []struct {
		name     string
		in       *Plugin
		expected string
	}{
		{
			name: "defaults",
			in: &Plugin{
				Command: []string{"app"},
			},
			expected: "app",
		},
		{
			name: "custom",
			in: &Plugin{
				Command: []string{"app"},
				Completion: PluginCompletion{
					Enabled: true,
					Command: []string{"runner", "__complete", "app"},
				},
			},
			expected: "runner",
		},
	} {
		t.Run(test.name, func(sub *testing.T) {
			if result := test.in.completionApp(); result != test.expected {
				sub.Errorf("expected: %v, got: %v", test.expected, result)
			}
		})
	}
}

func TestPlugin_complete(t *testing.T) {
	t.Run("defaults", func(sub *testing.T) {
		plugin := WithValidPlugin(sub)
		if err := plugin.complete(WithStubCmd(sub), []string{"example"}); err != nil {
			sub.Errorf("got error: %v", err)
		}
	})

	t.Run("custom", func(sub *testing.T) {
		plugin := WithValidPlugin(sub)
		plugin.Completion.Command = plugin.Command
		if err := plugin.complete(WithStubCmd(sub), []string{"example"}); err != nil {
			sub.Errorf("got error: %v", err)
		}
	})
}

func TestPlugin_execute(t *testing.T) {
	t.Run("ok", func(sub *testing.T) {
		plugin := WithValidPlugin(sub)
		plugin.Command = append(plugin.Command, "test")
		if err := plugin.execute(WithStubCmd(sub), []string{"example"}); err != nil {
			sub.Errorf("got error: %v", err)
		}
	})
}

func TestPlugin_Execute(t *testing.T) {
	t.Run("app", func(sub *testing.T) {
		plugin := WithValidPlugin(sub)
		if err := plugin.Execute(WithStubCmd(sub), []string{"example"}); err != nil {
			sub.Errorf("got error: %v", err)
		}
	})

	t.Run("script", func(sub *testing.T) {
		plugin := WithValidPlugin(sub)
		plugin.Command = []string{testScriptFile}
		err := plugin.Execute(WithStubCmd(sub), []string{"example"})

		switch goos() {
		case "windows":
			sub.Skip("running on windows, this test is going to be failed without additional preparations " +
				"=> it will be skipped")
			sub.SkipNow()
		default:
			if err != nil {
				sub.Errorf("got error: %v", err)
			}
		}
	})

	t.Run("unknown", func(sub *testing.T) {
		plugin := &Plugin{Command: []string{"plugin"}}
		err := plugin.Execute(WithStubCmd(sub), []string{"example"})
		if !errors.Is(ErrUnsupportedAppType, err) {
			sub.Errorf("expected: `%v`, got: `%v`", ErrUnsupportedAppType, err)
		}
	})
}
